#include "game.h"

#include <iostream>
#include <sstream>
#include <string>

#include <OgreCamera.h>
#include <OgreCgPlugin.h>
#include <OgreD3D9Plugin.h>
#include <OgreEntity.h>
#include <OgreGLPlugin.h>
#include <OgreHardwarePixelBuffer.h>
#include <OgreImage.h>
#include <OgreLogManager.h>
#include <OgreManualObject.h>
#include <OgreMaterialManager.h>
#include <OgreMesh.h>
#include <OgreRenderWindow.h>
#include <OgreRoot.h>
#include <OgreStaticGeometry.h>
#include <OgreTextureManager.h>
#include <OgreWindowEventUtilities.h>

#include <OISKeyboard.h>
#include <OISInputManager.h>

#include "cas.h"
#include "descr/descr_parser.h"
#include "descr/descr_character_parser.h"
#include "descr/descr_cultures_parser.h"
#include "descr/descr_model_strat_parser.h"
#include "descr/descr_regions_parser.h"
#include "descr/descr_settlement_mechanics_parser.h"
#include "descr/descr_sm_factions_parser.h"
#include "descr/descr_strat_parser.h"
#include "region.h"
#include "path.h"
#include "util.h"


using namespace std;


const string MAP_CLIMATES     = "map_climates.tga";
const string MAP_HEIGHTS      = "map_heights.tga";
const string MAP_GROUND_TYPES = "map_ground_types.tga";
const string MAP_REGIONS	  = "map_regions.tga";
const string WATER_SURFACE    = "water_surface.tga";


const int   XZ_RESCALE = 16;
const float XZ_RESC_IN = 1.0f / XZ_RESCALE;


const int MAX_TEXTURE_SIZE = 4 * 1024;
const int MAX_TILE_SIZE    = 512;


float camDeltaX = 0;
float camDeltaY = 0;
float camDeltaZ = 0;
float camDeltaR = 0;
float camDeltaP = 0;

Season::Index season = (Season::Index) 0;
Ogre::SceneNode* terrainSceneNodes[Season::COUNT];

Ogre::Image mapClimates;
Ogre::Image mapHeights;
Ogre::Image mapGroundTypes;
Ogre::Image mapRegions;

Ogre::Root*         root;
Ogre::RenderWindow* renderWindow;
Ogre::SceneManager* sceneManager;
Ogre::Camera*	    camera;
Ogre::Light*		light;

OIS::InputManager* inputManager;
OIS::Keyboard*     keyboard;

bool quit = false;
bool restart = false;


class UIVector2
{
public:
	UIVector2(unsigned int x, unsigned int y) : x(x), y(y) {}

	unsigned int x;
	unsigned int y;
};


void Game::init()
{
	root = OGRE_NEW Ogre::Root();
	Ogre::Root::getSingleton().installPlugin(new Ogre::D3D9Plugin());
	Ogre::Root::getSingleton().installPlugin(new Ogre::GLPlugin());
	//Ogre::Root::getSingleton().installPlugin(new Ogre::CgPlugin());
	root->restoreConfig();

	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(Path::CAMPAIGN,		                   "FileSystem", Path::CAMPAIGN);
	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(Path::MODELS_STRAT_TEXTURES,			   "FileSystem", Path::MODELS_STRAT_TEXTURES);
	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(Path::MODELS_STRAT_RESIDENCES_TEXTURES, "FileSystem", Path::MODELS_STRAT_RESIDENCES_TEXTURES);
	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(Path::GROUND_TEXTURES,                  "FileSystem", Path::GROUND_TEXTURES);
	Ogre::ResourceGroupManager::getSingleton().addResourceLocation(Path::TREE_MODELS_TEXTURES,             "FileSystem", Path::TREE_MODELS_TEXTURES);
	Ogre::ResourceGroupManager::getSingleton().addResourceLocation("",                                     "FileSystem", "Campaign");
	Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}


void initRenderingSystem()
{
	renderWindow = root->initialise(true, "FTW");

	sceneManager = root->createSceneManager(Ogre::ST_GENERIC);

	camera = sceneManager->createCamera("Camera");

    Ogre::Viewport* viewport = renderWindow->addViewport(camera);
    viewport->setBackgroundColour(Ogre::ColourValue(0.0f, 0.0f, 0.0f));

	camera->setAspectRatio(Ogre::Real(viewport->getActualWidth()) / Ogre::Real(viewport->getActualHeight()));
	camera->setPosition(30.0f, 50.0f, 75.0f);
	camera->lookAt(30.0f, 50.0f, 0.0f);
	camera->pitch(Ogre::Radian(-0.55f));
	camera->setNearClipDistance(0.1f);
	sceneManager->getRootSceneNode()->createChildSceneNode()->attachObject(camera);
}


void initLightning()
{
	sceneManager->setShadowTechnique(Ogre::SHADOWTYPE_TEXTURE_MODULATIVE);
	sceneManager->setShadowFarDistance(100.0f);
	sceneManager->setShadowTextureSize(1024);

	sceneManager->setAmbientLight(Ogre::ColourValue(0.2f, 0.2f, 0.2f));

	light = sceneManager->createLight("Sun Light");
	light->setType(Ogre::Light::LT_DIRECTIONAL);
	//light->setDiffuseColour(1.0f, 0.0f, 0.0f);
	//light->setSpecularColour(0.0f, 1.0f, 0.0f);
	light->setDirection(Ogre::Vector3(-1.0f, -1.5f, -1.0f));
}


void Game::initInput()
{
    OIS::ParamList paramList;    
    size_t windowHandle = 0;
    std::ostringstream windowHndStr;
 
    renderWindow->getCustomAttribute("WINDOW", &windowHandle);
 
    windowHndStr << (unsigned int) windowHandle;
    paramList.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
 
	inputManager = OIS::InputManager::createInputSystem(paramList);
    if (inputManager->getNumberOfDevices(OIS::OISKeyboard) > 0) {
        keyboard = static_cast<OIS::Keyboard*>(inputManager->createInputObject(OIS::OISKeyboard, true));
		keyboard->setEventCallback(this);
    }
}


const int NEIGHBOUR_POSITIONS[][2] = 
{
	{-1, -1}, {0, -1}, {1, -1},
	{-1,  0},          {1,  0},
	{-1,  1}, {0,  1}, {1,  1}
};

const int NEIGHBOUR_POSITIONS_SIZE = sizeof(NEIGHBOUR_POSITIONS) / sizeof(int) / 2;

const int NORMAL_NEIGHBOUR_POSITIONS[][2] = 
{
	{1,  0}, {1,  1}, {0,  1}, {-1,  0}, {-1, -1}, {0, -1}
};

const int NORMAL_NEIGHBOUR_POSITIONS_SIZE = sizeof(NORMAL_NEIGHBOUR_POSITIONS) / sizeof(int) / 2;


const unsigned int SEA_COLOR     = 0x298ce9;
const unsigned int CAPITAL_COLOR = 0x000000;
const unsigned int PORT_COLOR    = 0xffffff;


void loadDescrFiles()
{
	ClimatesParser climatesParser;
	climatesParser.setBasePath(Path::DATA + "terrain\\aerial_map\\tree_models\\");
	climatesParser.open(Path::DATA + "descr_climates.txt");

	GroundTypesParser groundTypesParser;
	groundTypesParser.open(Path::DATA + "descr_aerial_map_ground_types.txt");

	DescrSettlementMechanicsParser descrSettlementMechanicsParser;
	descrSettlementMechanicsParser.open(Path::DATA + "descr_settlement_mechanics.xml");

	DescrCulturesParser descrCulturesParser;
	descrCulturesParser.setBasePath(Path::MOD);
	descrCulturesParser.open(Path::DATA + "descr_cultures.txt");

	DescrSmFactionsParser descrSmFactionsParser;
	descrSmFactionsParser.setBasePath(Path::DATA);
	descrSmFactionsParser.open(Path::DATA + "descr_sm_factions.txt");

	DescrModelStratParser descrModelStratParser;
	descrModelStratParser.setBasePath(Path::DATA);
	descrModelStratParser.open(Path::DATA + "descr_model_strat.txt");

	DescrCharacterParser descrCharacterParser;
	descrCharacterParser.open(Path::DATA + "descr_character.txt");


	DescrRegionsParser descrRegionsParser;
	descrRegionsParser.open(Path::CAMPAIGN + "descr_regions.txt");

	DescrStratParser descrStratParser;
	descrStratParser.open(Path::CAMPAIGN + "descr_strat.txt");
	//descrStratParser.open(Path::CAMPAIGN + "..\\campaign\\imperial_campaign\\descr_strat.txt");

	for (unsigned int y = 0; y < mapRegions.getHeight(); y++)
	{
		for (unsigned int x = 0; x < mapRegions.getWidth(); x++)
		{			
			unsigned int color = mapRegions.getColourAt(x, y, 0).getAsARGB() & 0xffffff;
			if (color == CAPITAL_COLOR)
			{
				for (int i = 0; i < NEIGHBOUR_POSITIONS_SIZE; i++)
				{
					color = mapRegions.getColourAt(x + NEIGHBOUR_POSITIONS[i][0], y + NEIGHBOUR_POSITIONS[i][1], 0).getAsARGB() & 0xffffff;
					if (color != SEA_COLOR && color != PORT_COLOR)
					{
						shared_ptr<Region> region = Region::get(color);
						region->capitalPosition.x = x;
						region->capitalPosition.y = y;
						break;
					}
				}
			}
			else if (color == PORT_COLOR)
			{
				for (int i = 0; i < NEIGHBOUR_POSITIONS_SIZE; i++)
				{
					color = mapRegions.getColourAt(x + NEIGHBOUR_POSITIONS[i][0], y + NEIGHBOUR_POSITIONS[i][1], 0).getAsARGB() & 0xffffff;
					if (color != SEA_COLOR && color != CAPITAL_COLOR)
					{
						shared_ptr<Region> region = Region::get(color);
						region->portPosition.x = x;
						region->portPosition.y = y;
						break;
					}
				}
			}

		}
	}
}


void loadMaps()
{
	mapClimates.load(MAP_CLIMATES,        Path::CAMPAIGN);
	mapHeights.load(MAP_HEIGHTS,          Path::CAMPAIGN);
	mapGroundTypes.load(MAP_GROUND_TYPES, Path::CAMPAIGN);
	mapRegions.load(MAP_REGIONS,          Path::CAMPAIGN);
}


float getHeightAt(unsigned int x, unsigned int y)
{
	return ((mapHeights.getColourAt(x, y, 0).getAsARGB() & 0xFF00) >> 8) / 64.0f;
}


Ogre::Vector2& getTextCoord(unsigned int tileIndex, unsigned int x, unsigned y, float xo, float yo, UIVector2& atlasDim, Ogre::Vector2& textCoord)
{
	textCoord.x = float((x + 0) % XZ_RESCALE) / XZ_RESCALE;
	textCoord.y = float((y + 3) % XZ_RESCALE) / XZ_RESCALE;

	textCoord.x = (textCoord.x * MAX_TILE_SIZE / atlasDim.x) + (float(1) / atlasDim.x) * ((tileIndex * MAX_TILE_SIZE) % atlasDim.x);
	textCoord.y = (textCoord.y * MAX_TILE_SIZE / atlasDim.y) + (float(1) / atlasDim.y) * ((tileIndex * MAX_TILE_SIZE) / atlasDim.x * MAX_TILE_SIZE);

	textCoord.x += xo * ((float(MAX_TILE_SIZE) / atlasDim.x) * XZ_RESC_IN);
	textCoord.y += yo * ((float(MAX_TILE_SIZE) / atlasDim.y) * XZ_RESC_IN);

	return textCoord;
}


void prepareCampaignTerrain()
{
	unsigned int seasonSize = mapClimates.getWidth() * mapClimates.getHeight();

	vector<unsigned int>      tileMap(seasonSize * Season::COUNT);
	map<string, unsigned int> atlasMap;

	unsigned int i = 0;
	for (int seasonIndex = 0; seasonIndex < Season::COUNT; seasonIndex++)
	{
		for (unsigned int y = 0; y < mapClimates.getHeight(); y++)
		{
			for (unsigned int x = 0; x < mapClimates.getWidth(); x++)
			{	
				unsigned int climateColor    = mapClimates.getColourAt(x, y, 0).getAsARGB();
				unsigned int groundTypeColor = mapGroundTypes.getColourAt(x, y, 0).getAsARGB();

				string texture;

				GroundType::Index groundTypeIndex = GroundType::get(groundTypeColor);
				if (groundTypeIndex != GroundType::INVALID)
				{
					shared_ptr<Climate> climate = Climate::get(climateColor);
					texture = climate->textures[seasonIndex][groundTypeIndex];
				}
				else
				{
					shared_ptr<Climate> climate = Climate::getClimate("default");
					texture = climate->textures[seasonIndex][GroundType::BEACH];		
				}
				unsigned int index;
				if (texture.size() > 0)
				{
					map<string, unsigned int>::iterator i = atlasMap.find(texture);
					if (i != atlasMap.end())
					{
						index = (*i).second;
					}
					else
					{
						index = atlasMap.size();
						atlasMap[texture] = index;
					}
				} 
				else 
				{
					index = -1;
				}
				tileMap[i++] = index;
			}
		}
	}

	UIVector2 atlasDim(MAX_TEXTURE_SIZE, MAX_TILE_SIZE + ((atlasMap.size() * MAX_TILE_SIZE / MAX_TEXTURE_SIZE) * MAX_TILE_SIZE) );

	string atlasName = "Campaing Terrain Atlas";
	Ogre::TexturePtr textureAtlasPtr;
	textureAtlasPtr = Ogre::TextureManager::getSingleton().createManual(atlasName, "Campaign", Ogre::TEX_TYPE_2D, atlasDim.x, atlasDim.y, 0, Ogre::PF_FLOAT32_RGB);

	vector<string> orderedTextures(atlasMap.size());
	for (map<string, unsigned int>::iterator i = atlasMap.begin(); i != atlasMap.end(); i++)
	{
		string textureFileName = (*i).first;
		unsigned int index = (*i).second;
		orderedTextures[index] = textureFileName;
	}

	for (unsigned int i = 0; i < orderedTextures.size(); i++)
	{	
		Ogre::Image image;
		image.load(orderedTextures[i], Path::GROUND_TEXTURES);
			
		unsigned int x = (i * MAX_TILE_SIZE) % atlasDim.x;
		unsigned int y = (i * MAX_TILE_SIZE) / atlasDim.x * MAX_TILE_SIZE;
		Ogre::Image::Box box(x, y, x + MAX_TILE_SIZE, y + MAX_TILE_SIZE); 

		textureAtlasPtr->getBuffer()->blitFromMemory(image.getPixelBox(), box);
	}

	for (int seasonIndex = 0; seasonIndex < Season::COUNT; seasonIndex++)
	{
		ostringstream stream;
		stream << "Campaign Terrain Season " << seasonIndex;

		vector<Ogre::Vector3> normals(seasonSize, Ogre::Vector3::ZERO);
		unsigned int heightIndex = 0;
 		Ogre::Plane plane;
		for (int z = 0; z < mapHeights.getHeight(); z++)
		{
			for (int x = 0; x < mapHeights.getWidth(); x++)
			{			
				float xx = x * 0.5f;
				float zz = z * 0.5f;

				Ogre::Vector3 normal = Ogre::Vector3::ZERO;
				Ogre::Vector3 center(xx, getHeightAt(x, z), zz);
				
				for (int i = 0; i < NORMAL_NEIGHBOUR_POSITIONS_SIZE; i++)
				{
					int x1 = x + NORMAL_NEIGHBOUR_POSITIONS[i][0];
					int z1 = z + NORMAL_NEIGHBOUR_POSITIONS[i][1];
					Ogre::Vector3 v1(x1 * 0.5f, getHeightAt(x1, z1), z1 * 0.5f);

					int x2 = x + NORMAL_NEIGHBOUR_POSITIONS[(i + 1) % NORMAL_NEIGHBOUR_POSITIONS_SIZE][0];
					int z2 = z + NORMAL_NEIGHBOUR_POSITIONS[(i + 1) % NORMAL_NEIGHBOUR_POSITIONS_SIZE][1];
					Ogre::Vector3 v2(x2 * 0.5f, getHeightAt(x2, z2), z2 * 0.5f);

					plane.redefine(v1, center, v2);
					plane.normalise();
					normal += plane.normal;
				}
				normal.normalise();
				normals[heightIndex++] = normal;
			}
		}

		string materialName = stream.str() + " Material";
		//Ogre::MaterialPtr materialPtr = Ogre::MaterialManager::getSingleton().getByName("BaseWhite");
		Ogre::MaterialPtr materialPtr = Ogre::MaterialManager::getSingleton().create(materialName, "Campaign");
		materialPtr->getTechnique(0)->getPass(0)->createTextureUnitState(atlasName);
		materialPtr->getTechnique(0)->getPass(0)->setTextureFiltering(Ogre::TFO_TRILINEAR);
		materialPtr->getTechnique(0)->getPass(0)->setTextureAnisotropy(2);
		//materialPtr->getTechnique(0)->getPass(0)->setPolygonMode(Ogre::PM_WIREFRAME);

		Ogre::ManualObject* terrainObject = sceneManager->createManualObject(stream.str() + " ManualObject");
		terrainObject->begin(materialPtr->getName(), Ogre::RenderOperation::OT_TRIANGLE_LIST);
		//terrainObject->begin("BaseWhite", Ogre::RenderOperation::OT_TRIANGLE_LIST);

		for (unsigned int z = 0; z < mapHeights.getHeight() - 1; z++)
		{
			for (unsigned int x = 0; x < mapHeights.getWidth() - 1; x++)
			{
				float x1 = x * 0.5f;
				float z1 = z * 0.5f;

				unsigned int x2 = (x & 0xfffffffe) + 1;
				unsigned int z2 = (z & 0xfffffffe) + 1;
									 
				unsigned int color = mapGroundTypes.getColourAt(x2, z2, 0).getAsARGB();
				
				float r = mapGroundTypes.getColourAt(x2, z2, 0).r;
				float g = mapGroundTypes.getColourAt(x2, z2, 0).g;
				float b = mapGroundTypes.getColourAt(x2, z2, 0).b;

				if (GroundType::get(color) != GroundType::SEA_SHALLOW && GroundType::get(color) != GroundType::OCEAN)
				{
					unsigned int tileIndex = tileMap[(z2 * mapHeights.getWidth()) + x2 + (seasonSize * seasonIndex)];

					Ogre::Vector3 va(x1 + 0.0f, getHeightAt(x + 0, z + 0), z1 + 0.0f);
					Ogre::Vector3 vb(x1 + 0.5f, getHeightAt(x + 1, z + 0), z1 + 0.0f);
					Ogre::Vector3 vc(x1 + 0.0f, getHeightAt(x + 0, z + 1), z1 + 0.5f);
					Ogre::Vector3 vd(x1 + 0.5f, getHeightAt(x + 1, z + 1), z1 + 0.5f);

					Ogre::Vector2 textCoord;

					terrainObject->position(va);
					terrainObject->textureCoord(getTextCoord(tileIndex, x, z, 0.0f, 0.0f, atlasDim, textCoord));
					terrainObject->normal(normals[(x + 0) + ((z + 0) * mapHeights.getWidth())]);
					//terrainObject->colour(r, g, b);

					terrainObject->position(vd);
					terrainObject->textureCoord(getTextCoord(tileIndex, x, z, 1.0f, 1.0f, atlasDim, textCoord));
					terrainObject->normal(normals[(x + 1) + ((z + 1) * mapHeights.getWidth())]);
					//terrainObject->colour(r, g, b);

					terrainObject->position(vb);
					terrainObject->textureCoord(getTextCoord(tileIndex, x, z, 1.0f, 0.0f, atlasDim, textCoord));
					terrainObject->normal(normals[(x + 1) + ((z + 0) * mapHeights.getWidth())]);
					//terrainObject->colour(r, g, b);


					terrainObject->position(va);
					terrainObject->textureCoord(getTextCoord(tileIndex, x, z, 0.0f, 0.0f, atlasDim, textCoord));
					terrainObject->normal(normals[(x + 0) + ((z + 0) * mapHeights.getWidth())]);
					//terrainObject->colour(r, g, b);

					terrainObject->position(vc);
					terrainObject->textureCoord(getTextCoord(tileIndex, x, z, 0.0f, 1.0f, atlasDim, textCoord));
					terrainObject->normal(normals[(x + 0) + ((z + 1) * mapHeights.getWidth())]);
					//terrainObject->colour(r, g, b);

					terrainObject->position(vd);
					terrainObject->textureCoord(getTextCoord(tileIndex, x, z, 1.0f, 1.0f, atlasDim, textCoord));
					terrainObject->normal(normals[(x + 1) + ((z + 1) * mapHeights.getWidth())]);
					//terrainObject->colour(r, g, b);
				}
			}
		}
		terrainObject->end();
		terrainObject->setCastShadows(false);

		terrainSceneNodes[seasonIndex] = sceneManager->createSceneNode();		
		terrainSceneNodes[seasonIndex]->attachObject(terrainObject);
	}
	sceneManager->getRootSceneNode()->addChild(terrainSceneNodes[0]);

	Ogre::MaterialPtr materialPtr = Ogre::MaterialManager::getSingleton().create("Campaign Ocean Material", "Campaign");
	materialPtr->getTechnique(0)->getPass(0)->createTextureUnitState(WATER_SURFACE);

	Ogre::ManualObject* waterObject = sceneManager->createManualObject("Campaign Water Mesh");
	waterObject->begin(materialPtr->getName(), Ogre::RenderOperation::OT_TRIANGLE_STRIP);

	Ogre::Vector3 a(0, -0.1f, 0);
	Ogre::Vector3 b(float(mapRegions.getWidth()), -0.1f, 0);
	Ogre::Vector3 c(0, -0.1f, float(mapRegions.getHeight()));
	Ogre::Vector3 d(float(mapRegions.getWidth()), -0.1f, float(mapRegions.getHeight()));

	// Water
	{
		waterObject->position(a);
		waterObject->normal(-(a - c).crossProduct(d - c).normalisedCopy());
		waterObject->textureCoord(0.0f, 0.0f);

		waterObject->position(c);
		waterObject->normal(-(a - c).crossProduct(d - c).normalisedCopy());
		waterObject->textureCoord(0.0f, 1.0f);

		waterObject->position(b);
		waterObject->normal(-(a - c).crossProduct(d - c).normalisedCopy());
		waterObject->textureCoord(1.0f, 0.0f);

		waterObject->position(d);
		waterObject->normal(-(a - c).crossProduct(d - c).normalisedCopy());
		waterObject->textureCoord(1.0f, 1.0f);

		waterObject->end();
		waterObject->setCastShadows(false);
		sceneManager->getRootSceneNode()->createChildSceneNode()->attachObject(waterObject);
	}

	Ogre::StaticGeometry* treeStaticGeometry = sceneManager->createStaticGeometry("Campaign Trees");
	treeStaticGeometry->setRegionDimensions(Ogre::Vector3(float(mapRegions.getWidth()), 5, float(mapRegions.getHeight())));
	treeStaticGeometry->setOrigin(Ogre::Vector3(0.0f, 0.0f, 0.0f));

	//map<unsigned int, shared_ptr<Climate>>& colorMap = Climate::getColorMap();
	//for (map<unsigned int, std::shared_ptr<Climate>>::iterator i = colorMap.begin(); i != colorMap.end(); i++)
	//{
	//	shared_ptr<Climate> climate = (*i).second;
	//	if (climate.get() != NULL)
	//	{
	//		shared_ptr<Cas> cas = climate->sparseTree[Season::SUMMER];
	//		if (cas.get() != NULL)
	//		{
	//			unsigned int climateColor = (*i).first;
	//			for (unsigned int z = 0; z < mapClimates.getHeight() - 1; z += 2)
	//			{
	//				for (unsigned int x = 0; x < mapClimates.getWidth() - 1; x += 2)
	//				{
	//					if (climateColor == mapClimates.getColourAt(x, z, 0).getAsARGB())
	//					{
	//						if (GroundType::get(mapGroundTypes.getColourAt(x, z, 0).getAsARGB()) == GroundType::FOREST_SPARSE)
	//						{
	//							Ogre::Entity* entity = sceneManager->createEntity(cas->getMeshName());

	//							//Ogre::SceneNode* treeSceneNode = sceneManager->getRootSceneNode()->createChildSceneNode();
	//							//treeSceneNode->attachObject(entity);

	//							Ogre::Vector3 position((x * 0.5f), getHeightAt(x, z), (z * 0.5f));
	//							treeStaticGeometry->addEntity(entity, position);
	//						}
	//					}
	//				}
	//			}
	//		}
	//	}
	//}
	treeStaticGeometry->setCastShadows(true);
	treeStaticGeometry->build();
}


void prepareRegions()
{
	map<string, shared_ptr<Faction>>& factionMap = Faction::getFactionMap();
	for (map<string, shared_ptr<Faction>>::iterator i = factionMap.begin(); i != factionMap.end(); i++)
	{
		shared_ptr<Faction> faction = (*i).second;
		for (unsigned int j = 0; j < faction->regions.size(); j++)
		{
			std::shared_ptr<Region> region = faction->regions[j];
			string settlementTypeID = region->settlementTypeID;
			
			shared_ptr<Culture> culture = Culture::get(faction->cultureID);
			std::shared_ptr<Culture::Settlement> settlement = culture->settlementMap[settlementTypeID];

			int x = region->capitalPosition.x;
			int z = region->capitalPosition.y;
			float y = getHeightAt((x * 2) + 1, (z * 2) + 1);

			shared_ptr<Cas> cas = Cas::get(settlement->modelPath, Cas::Type::RTW);
			if (cas.get() != NULL)
			{
				Ogre::SceneNode* capitalSceneNode = sceneManager->getRootSceneNode()->createChildSceneNode(Ogre::Vector3(x + 0.5f, y, z + 0.5f));
				Ogre::Entity* entity = sceneManager->createEntity(cas->getMeshName());
				capitalSceneNode->attachObject(entity);
			}
		}
	}

	for (map<string, shared_ptr<Faction>>::iterator i = factionMap.begin(); i != factionMap.end(); i++)
	{
		shared_ptr<Faction> faction = (*i).second;
		shared_ptr<Cas> cas = Cas::get(faction->symbolPath, Cas::Type::RTW);
		if (cas.get() != NULL)
		{
			for (unsigned int j = 0; j < faction->regions.size(); j++)
			{
				std::shared_ptr<Region> region = faction->regions[j];

				int x = region->capitalPosition.x;
				int z = region->capitalPosition.y;
				float y = getHeightAt((x * 2) + 1, (z * 2) + 1);

				Ogre::Entity* entity = sceneManager->createEntity(cas->getMeshName());

				Ogre::SceneNode* bannerSceneNode = sceneManager->getRootSceneNode()->createChildSceneNode(Ogre::Vector3(x + 0.5f, y + 0.65f, z + 0.5f));
				bannerSceneNode->attachObject(entity);
			}
		}
	}
}


void prepareCharacters()
{
	map<string, shared_ptr<Faction>>& factionMap = Faction::getFactionMap();
	for (map<string, shared_ptr<Faction>>::iterator i = factionMap.begin(); i != factionMap.end(); i++)
	{
		shared_ptr<Faction> faction = (*i).second;

		for (unsigned int i = 0; i < faction->characters.size(); i++)
		{
			shared_ptr<Character> character = faction->characters[i];

			string modelID = character->type->getStratModelID(faction->id, character->level);
			shared_ptr<Cas> cas = ModelStrat::get(modelID)->cas;
			if (cas.get() == NULL)
			{
				continue;
			}
			else
			{
				cout << "T";
			}


			int x = character->x;
			int z = character->y;
			float y = getHeightAt((x * 2) + 1, (z * 2) + 1);

			Ogre::Entity* entity = sceneManager->createEntity(cas->getMeshName());

			Ogre::SceneNode* characterSceneNode = sceneManager->getRootSceneNode()->createChildSceneNode(Ogre::Vector3(x + 0.5f, y, z + 0.5f));
			characterSceneNode->attachObject(entity);
		}
	}
}


bool Game::start() 
{
	restart = false;

	init();
	initRenderingSystem();
	//Ogre::TextureManager::getSingleton().setDefaultNumMipmaps(4);
	loadMaps();
	loadDescrFiles();
	initLightning();

	
	Ogre::LogManager::getSingleton().logMessage(Ogre::LML_NORMAL, "prepareCampaignTerrain()");
	prepareCampaignTerrain();
	Ogre::LogManager::getSingleton().logMessage(Ogre::LML_NORMAL, "prepareCampaignTerrain() - Done");

	Ogre::LogManager::getSingleton().logMessage(Ogre::LML_NORMAL, "prepareRegions()");
	prepareRegions();
	Ogre::LogManager::getSingleton().logMessage(Ogre::LML_NORMAL, "prepareRegions() - Done");

	Ogre::LogManager::getSingleton().logMessage(Ogre::LML_NORMAL, "prepareCharacters()");
	prepareCharacters();
	Ogre::LogManager::getSingleton().logMessage(Ogre::LML_NORMAL, "prepareCharacters() - Done");


	initInput();

	root->clearEventTimes();
	root->addFrameListener(this);

	Ogre::LogManager::getSingleton().logMessage(Ogre::LML_NORMAL, "startRendering()");
	root->startRendering();

	{
		inputManager->destroyInputObject(keyboard);
		OIS::InputManager::destroyInputSystem(inputManager);

		root->removeFrameListener(this);

		//sceneManager->destroyAllCameras();
		//sceneManager->getRootSceneNode()->removeAndDestroyAllChildren();
		//sceneManager->destroyAllStaticGeometry();
		//sceneManager->destroyAllManualObjects();	
		//sceneManager->clearScene();

		//renderWindow->removeAllViewports();
		//renderWindow->destroy();

		//Ogre::WindowEventUtilities::removeWindowEventListener(renderWindow, this);
	}

	//OGRE_DELETE root;
	return restart;
}


bool Game::frameRenderingQueued(const Ogre::FrameEvent& evt)
{
	if (quit)
	{
		return false;
	}

	keyboard->capture();

	//Ogre::Radian angle(-0.785f);
	//camera->pitch(-angle);
	//camera->move(Ogre::Vector3(0, 0, 0));
	camera->moveRelative(Ogre::Vector3(camDeltaX, camDeltaY, camDeltaZ));
	camera->yaw(Ogre::Radian(camDeltaR));
	camera->pitch(Ogre::Radian(camDeltaP));
	//camera->pitch(angle);

	return true;
}


bool Game::keyPressed(const OIS::KeyEvent& keyEvent)
{
	switch (keyEvent.key)
	{
		case OIS::KC_A: camDeltaX = -0.7f; break;
		case OIS::KC_D: camDeltaX = +0.7f; break;
		case OIS::KC_W: camDeltaZ = -0.7f; break;
		case OIS::KC_S: camDeltaZ = +0.7f; break;

		case OIS::KC_DOWN:  camDeltaP = -0.04f; break;
		case OIS::KC_UP:    camDeltaP = +0.04f; break;
		case OIS::KC_LEFT:  camDeltaR = +0.04f; break;
		case OIS::KC_RIGHT: camDeltaR = -0.04f; break;

		case OIS::KC_TAB:
		{
			sceneManager->getRootSceneNode()->removeChild(terrainSceneNodes[season]);
			season = (Season::Index) ((season + 1) % Season::COUNT);
			sceneManager->getRootSceneNode()->addChild(terrainSceneNodes[season]);
		}
		break;
	}
	return true;
}


bool Game::keyReleased(const OIS::KeyEvent& keyEvent)
{
	switch (keyEvent.key)
	{
		case OIS::KC_ESCAPE: quit = true; break;
		case OIS::KC_R:      restart = true; break;

		case OIS::KC_A: camDeltaX = 0.0f; break;
		case OIS::KC_D: camDeltaX = 0.0f; break;
		case OIS::KC_W: camDeltaZ = 0.0f; break;
		case OIS::KC_S: camDeltaZ = 0.0f; break;

		case OIS::KC_DOWN:  camDeltaP = 0.0f; break;
		case OIS::KC_UP:    camDeltaP = 0.0f; break;
		case OIS::KC_LEFT:  camDeltaR = 0.0f; break;
		case OIS::KC_RIGHT: camDeltaR = 0.0f; break;
	}
	return true;
}
